import type { IMenu } from '@/types/api';
import type { CustomRouteObject } from '@/types/react-router-dom';

/**
 * 业务异常类
 * @description 用于表示业务逻辑处理中的错误，包含错误码和可选错误消息。
 */
export class BizError extends Error {
    // 业务处理状态码
    code: string;
    // 是否错误信息弹框
    handled?: boolean;
    constructor(code: string, message?: string, handled = false) {
        super(message ?? String(code));
        this.code = String(code);
        this.handled = handled;
        this.name = 'BizError';
        Object.setPrototypeOf(this, BizError.prototype);
    }
}

/**
 * 根据运行环境，获取后端接口请求基础路径
 */
export const getBaseURL = () => {
    return import.meta.env.VITE_APP_USE_MOCK === 'true'
        ? import.meta.env.VITE_APP_MOCK_API_BASEURL
        : import.meta.env.VITE_APP_API_BASEURL;
};

/**
 * 格式化为人民币货币字符串，例如：¥1,234.56
 */
export const formatMoney = (num?: number | string): string => {
    const number = Number(num);
    if (isNaN(number)) return '¥0.00';
    return number.toLocaleString('zh-CN', { style: 'currency', currency: 'CNY' });
};

/**
 * 格式化为带千位分隔符的字符串，例如：1,234.56
 */
export const formatNum = (num?: number | string) => {
    const number = Number(num);
    if (isNaN(number)) return '0.00';
    return number.toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
};

/**
 * 格式化日期为本地字符串
 */
export const toLocalDate = (date?: Date | string, rule?: string): string => {
    let curDate = date ? new Date(date) : new Date();
    // 判断当前日期对象 curDate 是否为无效日期，如果无效则重置为当前时间。
    if (isNaN(curDate.getTime())) curDate = new Date();

    if (rule === 'yyyy-MM-dd') return curDate.toLocaleDateString().replaceAll('/', '-');
    if (rule === 'HH:mm:ss') return curDate.toLocaleTimeString().replaceAll('/', '-');
    return curDate.toLocaleString().replaceAll('/', '-');
};

/**
 * 自定义格式化日期
 */
export const formatDate = (date?: Date | string, rule = 'yyyy-MM-dd HH:mm:ss'): string => {
    let curDate = date ? new Date(date) : new Date();
    // 判断当前日期对象 curDate 是否为无效日期，如果无效则重置为当前时间。
    if (isNaN(curDate.getTime())) curDate = new Date();

    let fmt = rule.replace(/(y+)/, curDate.getFullYear().toString());
    const O: Record<string, number> = {
        'M+': curDate.getMonth() + 1,
        'd+': curDate.getDate(),
        'H+': curDate.getHours(),
        'm+': curDate.getMinutes(),
        's+': curDate.getSeconds()
    };
    for (const k in O) {
        fmt = fmt.replace(new RegExp(`(${k})`), O[k] > 9 ? O[k].toString() : '0' + O[k]);
    }
    return fmt;
};

/**
 * 用户状态转换
 */
export const formatState = (state: string): string => {
    switch (state) {
        case '1':
            return '在职';
        case '2':
            return '试用期';
        case '3':
            return '离职';
        default:
            return '-';
    }
};

/**
 * 手机号加密 17611000011 => 176****0011
 */
export const formateMobile = (mobile?: number | string): string => {
    if (!mobile) return '-';
    const phone = mobile.toString();
    return phone.replace(/(\d{3})\d*(\d{4})/, '$1****$2');
};

/**
 * 获取所有菜单路径
 * @description 递归获取所有菜单路径，供路由权限判断使用
 * @example ['/system/orderList','/system/roleList','/system/userList','/system/menuList']
 */
export const getMenuPath = (list: IMenu.MenuItem[]): string[] => {
    /**
     * 后端约定返回的菜单数据结构中，
     * 末级菜单有 buttons 属性，中间节点没有 buttons 属性，
     * 且末级菜单的path为完整绝对路径
     */
    return list.reduce<string[]>((result, item) => {
        // 如果当前项有children且没有buttons属性,则为中间节点
        if (Array.isArray(item.children) && !item.buttons) {
            return result.concat(getMenuPath(item.children));
        }
        // 否则为末级菜单
        return result.concat(item.path || '');
    }, []);
};

/**
 * 标准化路径，去除末尾斜杠和查询参数
 * @description 例如 : '/userList/' => '/userList' 或者 '/userList?query=1' => '/userList'
 * @param path
 * @returns
 */
export const normalizePath = (path: string) => path.replace(/\/+$/, '').split('?')[0];

/**
 * 递归获取路由对象
 * @param path
 * @param routes
 * @returns
 */
export const searchRoute = (path: string, routes: CustomRouteObject[] = []): CustomRouteObject | null => {
    // 本地标准化函数（避免依赖文件后面定义的 normalizePath 导致 TDZ 问题）
    const normalize = (path: string) => (path || '').replace(/\/+$/, '').split('?')[0];
    // 目标标准化路径
    const target = normalize(path);

    const _search = (list: CustomRouteObject[], parentPath = ''): CustomRouteObject | null => {
        for (const item of list) {
            const raw = item.path ?? '';
            // 计算当前项的完整路径：
            let curPath = '';
            if (raw.startsWith('/')) {
                curPath = normalize(raw);
            } else if (parentPath) {
                curPath = normalize(`${parentPath.replace(/\/$/, '')}/${raw.replace(/^\//, '')}`);
            } else {
                // 顶层且为相对 path，则补全为 '/xxx'（保持和路由匹配逻辑一致）
                curPath = normalize(raw ? `/${raw.replace(/^\//, '')}` : '');
            }

            // 匹配到则返回当前项
            if (curPath === target) return item;

            // 递归查找子路由，传入当前完整路径作为 parentPath
            if (item.children && item.children.length) {
                const res = _search(item.children, curPath);
                if (res) return res;
            }
        }
        return null;
    };

    return _search(routes, '');
};

/**
 * 递归查找树节点，返回从根节点到目标节点的路径名称数组
 * @param tree - 树结构数组
 * @param pathname  - 目标路径
 * @returns
 */
export const findTreeNode = (tree: IMenu.MenuItem[] = [], pathname = ''): { menuName: string; path: string }[] => {
    // 本地标准化函数（避免依赖文件后面定义的 normalizePath 导致 TDZ 问题）
    const normalize = (ptah: string) => (ptah || '').replace(/\/+$/, '').split('?')[0];
    // 目标标准化路径
    const target = normalize(pathname);
    // DFS 搜索路径栈
    const stack: { menuName: string; path: string }[] = [];
    // 结果路径数组
    let result: { menuName: string; path: string }[] = [];

    const dfs = (list: IMenu.MenuItem[] = [], parentPath = ''): boolean => {
        for (const node of list) {
            // 安全读取 path（可能不存在或非字符串）
            const raw = typeof node.path === 'string' ? node.path : '';
            let curPath = '';

            if (raw) {
                if (raw.startsWith('/')) {
                    curPath = normalize(raw);
                } else if (parentPath) {
                    curPath = normalize(`${parentPath.replace(/\/$/, '')}/${raw.replace(/^\//, '')}`);
                } else {
                    curPath = normalize(`/${raw.replace(/^\//, '')}`);
                }
            } else {
                // 没有 path 的节点使用父路径（例如分组）
                curPath = parentPath || '';
            }

            // 读取标题（IMenu.MenuItem 应包含 menuName）
            const menuName = node.menuName || '';

            stack.push({ menuName, path: curPath });

            if (curPath && curPath === target) {
                result = stack.slice();
                return true;
            }

            if (Array.isArray(node.children) && node.children.length) {
                if (dfs(node.children, curPath)) return true;
            }

            stack.pop();
        }
        return false;
    };

    dfs(tree, '');
    return result;
};
